#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>

#include "ak_common.h"
#include "ak_vi.h"
#include "ak_ai.h"
#include "ak_ao.h"
#include "ak_sd_card.h"
#include "ak_dvr_record.h"

#define MAIN_WIDTH          1280
#define MAIN_HEIGHT         960
#define SUB_WIDTH           640
#define SUB_HEIGHT          480

/* ISP config file path */
#define FIRST_PATH       				"/etc/jffs2/"
#define BACK_PATH        				"/usr/local/"

static void *dvr_vi_handle = NULL;
static void *dvr_ai_handle = NULL;
static enum dvr_record_type trigger_type = RECORD_TRIGGER_TYPE_PLAN;
static int duration = 300;		//duration of each record file(unit: second)
static int alarm_pre_time = 2; //keeping video and audio before alarm record trigger
static int alarm_stop_time = 60;//stop record after keep silence time

static int init_vi(void)
{
	/* open video device */
	dvr_vi_handle = ak_vi_open( VIDEO_DEV0 );
	if (NULL == dvr_vi_handle) {
		ak_print_error_ex("vi open failed\n");
		return AK_FAILED;
	}

	/* get camera resolution */
	struct video_resolution resolution = {0};
	if (ak_vi_get_sensor_resolution( dvr_vi_handle, &resolution ))
		ak_print_error_ex("get sensor resolution failed\n");

	/* set crop information */
	struct video_channel_attr attr;
	int main_width = MAIN_WIDTH;
	int main_height = MAIN_HEIGHT;
	int sub_width = SUB_WIDTH;
	int sub_height = SUB_HEIGHT;

	memset( &attr, 0x00, sizeof(attr) );
	attr.res[VIDEO_CHN_MAIN].width = main_width;
	attr.res[VIDEO_CHN_MAIN].height = main_height;
	attr.res[VIDEO_CHN_SUB].width = sub_width;
	attr.res[VIDEO_CHN_SUB].height = sub_height;
	attr.crop.left = 0;
	attr.crop.top = 0;
	attr.crop.width = main_width;
	attr.crop.height = main_height;

	if (ak_vi_set_channel_attr( dvr_vi_handle, &attr )) {
		ak_print_error_ex("set channel attribute failed\n");
	}

	/* get crop */
	struct video_channel_attr cur_attr;

	memset( &cur_attr, 0x00, sizeof( cur_attr ));
	if (ak_vi_get_channel_attr( dvr_vi_handle, &cur_attr )) {
		ak_print_normal("ak_vi_get_channel_attr failed!\n");
	}

	ak_print_normal_ex("capture fps: %d\n", ak_vi_get_fps(dvr_vi_handle));
	ak_print_normal("\nvideo input info:\n"
			"\tmain_w[%d], main_h[%d], sub_w[%d], sub_h[%d]\n\n",
			main_width, main_height,
			sub_width, sub_height);

	return AK_SUCCESS;
}

/* match sensor according to ISP config file path */
static int match_sensor(const char *isp_cfg_path)
{
	DIR *dir = opendir( isp_cfg_path );
	if (NULL == dir) {
		ak_print_normal_ex("it fails to open directory %s\n", isp_cfg_path );
		return 0;
	}

	int ret = AK_FAILED;
	char isp_file[255] = {0};
	char *tmp = NULL;
	char *isp_cfg = NULL;
	struct dirent *dir_ent = NULL;

	while ( NULL != ( dir_ent = readdir( dir ))) {
		if (!dir_ent->d_name)
			continue;

		/* fine next when we get dir */
        if ((dir_ent->d_type & DT_DIR)) {
            continue;
        }

		/* make sure use isp_*.conf file to match */
		tmp = strstr(dir_ent->d_name, "isp_");
		if (!tmp) {
			continue;
		}

		isp_cfg = strstr(tmp, ".conf");
		if (!isp_cfg) {
			continue;
		}

		sprintf( isp_file, "%s%s", isp_cfg_path, dir_ent->d_name );
		/* get sensor id, match config file */
		if(AK_SUCCESS == ak_vi_match_sensor( isp_file )){
			ak_print_notice_ex("ak_vi_match_sensor OK\n");
			ret = AK_SUCCESS;

			if (strcmp(isp_cfg_path, FIRST_PATH))
			{
				char cmd[128] = {0};

				sprintf(cmd, "cp %s %s", isp_file, FIRST_PATH);
				system(cmd);

				sprintf( isp_file, "%s%s", FIRST_PATH, dir_ent->d_name );
				ak_vi_match_sensor( isp_file );
			}
			break;
		}
	}
	closedir(dir);

	return ret;
}

static int init_video(void)
{
	/* match sensor at the first step */
	if (AK_FAILED == match_sensor(FIRST_PATH)) {
		ak_print_warning_ex("match_sensor FIRST_PATH failed\n");
		if (AK_FAILED == match_sensor(BACK_PATH)) {
			ak_print_error_ex("match_sensor BACK_PATH failed\n");
			return AK_FAILED;
		}
	}

	int ret = AK_FAILED;

	/* one video input device, only open one time vi for encode */
	if (init_vi()) {
		ak_print_error_ex("video input init faild, exit\n");
	} else {
		ak_print_notice_ex("start capture ...\n");
		if(ak_vi_capture_on( dvr_vi_handle )) {
			ak_print_error_ex("vi capture on failed\n");
		} else {
			ret = AK_SUCCESS;
		}
	}

	if ((AK_FAILED == ret) && (NULL != dvr_vi_handle)) {
		ak_vi_close(dvr_vi_handle);
		dvr_vi_handle = NULL;
	}

	return ret;
}

static int init_ai(void)
{
	struct pcm_param ai_param = {0};

	/* set correct param to open audio input */
	ai_param.sample_bits = 16;
	/* channel number only support AUDIO_CHANNEL_MONO */
	ai_param.channel_num = AUDIO_CHANNEL_MONO;
	ai_param.sample_rate = 8000;

	dvr_ai_handle = ak_ai_open(&ai_param);
	if(NULL == dvr_ai_handle){
		return AK_FAILED;
	}

	/* set ai attributions after open OK */
	ak_ai_set_aec(dvr_ai_handle, AUDIO_FUNC_ENABLE);
	ak_ai_set_nr_agc(dvr_ai_handle, AUDIO_FUNC_ENABLE);
	ak_ai_set_resample(dvr_ai_handle, AUDIO_FUNC_ENABLE);
	ak_ai_set_volume(dvr_ai_handle, 8);
	ak_ai_clear_frame_buffer(dvr_ai_handle);

	return AK_SUCCESS;
}


static int init_audio(void)
{
	if (AK_FAILED == init_ai()) {
		ak_print_error_ex("init ai failed\n");
		return AK_FAILED;
	}
	ak_print_notice_ex("init audio in OK\n");

	return AK_SUCCESS;
}

static int init_record(void)
{
	int ret = AK_SUCCESS;

	/* video record param init */
	struct record_param record;

	record.vi_handle = dvr_vi_handle;
	record.ai_handle = dvr_ai_handle;

	record.width = MAIN_WIDTH;
	record.height = MAIN_HEIGHT;
	record.enc_chn = ENCODE_MAIN_CHN;

	record.file_fps = 10;
	record.file_bps = 800;
	record.gop_len = 2;
	
	/* audio sample and frame interval */
	record.sample_rate = 8000;
	record.frame_interval = 100;

	record.cyc_flag = 1;
	record.file_type = DVR_FILE_TYPE_AVI;
    record.duration = (300 * 1000);
    strcpy(record.rec_prefix, "CYC_DV_");
    strcpy(record.rec_path, "/mnt/CYC_DV/");
    trigger_type = RECORD_TRIGGER_TYPE_PLAN;
	duration = 300;
	alarm_pre_time = 2;
	alarm_stop_time = 60;
	record.error_report = NULL;
	ret = ak_ai_set_frame_interval( dvr_ai_handle, record.frame_interval );
	if (AK_SUCCESS == ret) {
		ak_print_normal("ai frame interval=%d\n", record.frame_interval);
		ret = ak_ai_start_capture(dvr_ai_handle);
		if (AK_SUCCESS == ret){
    		ret = ak_dvr_record_init(&record);
		}
	}

	return ret;
}

static void usage(const char *name)
{
	ak_print_normal("Usage: %s [options]\n\n"
		"options:\n"
		"-h   help\n"
		"this program is to do video record.\n"
		"record param is got from /etc/jffs2/anyka_cfg.ini.\n",name);
}

static int parse_args(int argc, char **argv)
{
	int ch;
	/* parse input options */
	while ((ch = getopt(argc, argv, "h")) != EOF) {
		switch (ch) {
			case 'h':
			case 'H':
			default:
				usage( argv[0] );
				exit( 0 );
				break;
		}
	}

	return 0;
}

int main (int argc, char **argv)
{
	ak_print_normal_ex("dvr record demo start\n");
	/* parse the arg which come from commad line*/
	parse_args(argc, argv);

	/* step 1 init video in */
	if(init_video()) {
		ak_print_error_ex("init_video failed\n");
		goto FAILED;
	}
	/* step 2 init audio in */
	if(init_audio()) {
		ak_print_error_ex("init_video failed\n");
		goto FAILED;
	}
	/* step 3 check card status  */
	if(SD_STATUS_CARD_INSERT & ak_sd_check_insert_status()){
		ak_print_normal_ex("SD Card Inserted\n");
		if(AK_SUCCESS == ak_sd_check_mount_status()){
			ak_print_normal_ex("SD Card mounted\n");
		}
		else{
			ak_print_normal_ex("SD Card not mounted\n");
			goto FAILED;
		}
	}
	else{
		ak_print_normal_ex("SD Card not Inserted\n");
		goto FAILED;
	}
	/* step 4 init record param  */
	if (AK_FAILED == init_record()){
		ak_print_normal_ex("init_record failed.\n");
		goto FAILED;
	}

	/* keeping video and audio before alarm record trigger */
	if (RECORD_TRIGGER_TYPE_ALARM == trigger_type){
		ak_dvr_record_turn_on_trigger(alarm_pre_time);
		ak_sleep_ms( alarm_pre_time * 1000 );
	}
	/* step 5 start to record video to file*/
	if (AK_FAILED == ak_dvr_record_start( trigger_type )){
		ak_print_normal_ex("init_record failed.\n");
		goto FAILED;
	}
	if (RECORD_TRIGGER_TYPE_ALARM == trigger_type){
		ak_sleep_ms( alarm_stop_time * 1000 );
	}
	else{
		ak_sleep_ms( duration * 1000 );
	}

	if (RECORD_TRIGGER_TYPE_ALARM == trigger_type){
		ak_dvr_record_turn_off_trigger();
	}
	/* step 6 stop to record video to file*/
	ak_dvr_record_stop();
	/* step 7 exit  record and release resource */
	ak_dvr_record_exit();
	ak_print_normal_ex("dvr record demo end\n");

FAILED:
	return 0;
}
